#pragma once

#include <iostream>
#include <unistd.h>
#include <cstdlib>
#include <cstring>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "err.hpp"
#include <functional>
#include <signal.h>

namespace ns_server{
    using namespace std;
    static const uint16_t defaultPort = 8086;
    static const int backlog = 32;

    using func_t = function<string(const string&)>;

    class TcpServer;
    class ThreadData{
        public:
            ThreadData(int fd, const string &ip, const uint16_t& port, TcpServer* ts)
            :sock(fd), clientIp(ip), clientPort(port), current(ts){

            }

            int sock;
            string clientIp;
            uint16_t clientPort;
            TcpServer *current;
    };

    class TcpServer{
        public:
            TcpServer(func_t func, uint16_t port = defaultPort) : func_(func), port_(port), quit_(true){

            }

            void initServer(){
                //1.创建socket, 文件
                listenSock_ = socket(AF_INET, SOCK_STREAM, 0);
                if(listenSock_ < 0){
                    cerr << "create socket error" << endl;
                    exit(SOCKET_ERR);
                }

                //2.bind
                struct sockaddr_in local;
                memset(&local, 0, sizeof(local));
                local.sin_family = AF_INET;
                local.sin_port = htons(port_);
                local.sin_addr.s_addr = htonl(INADDR_ANY);
                if(bind(listenSock_, (struct sockaddr*)&local, sizeof(local)) < 0){
                    cerr << "bind socket error" << endl;
                    exit(BIND_ERR);
                }

                //3.监听
                if(listen(listenSock_, backlog) < 0){
                    cerr << "listen socket error" << endl;
                    exit(LISTEN_ERR);
                }
            }

            void start(){

                signal(SIGCHLD, SIG_IGN);

                quit_ = false;
                while(!quit_){
                    struct sockaddr_in client;
                    socklen_t len = sizeof(client);

                    //4.获取连接, accept
                    int sock = accept(listenSock_, (struct sockaddr*)&client, &len);
                    if(sock < 0){
                        cerr << "accept error" << endl;
                        continue;
                    }

                    //提取client信息
                    string clientIp = inet_ntoa(client.sin_addr);
                    uint16_t clientPort = ntohs(client.sin_port);

                    //获取连接成功，开始业务处理
                    cout << "success: " << sock << " from " << listenSock_ << ", " << clientIp << clientPort << endl;

                    //多线程
                    pthread_t tid;
                    ThreadData* td = new ThreadData(sock, clientIp, clientPort, this);
                    pthread_create(&tid, nullptr, threadRoutine, td);
                }
            }

            static void* threadRoutine(void* args){
                //线程分离，不需要回收
                pthread_detach(pthread_self());

                ThreadData* td = static_cast<ThreadData*>(args);
                td->current->service(td->sock, td->clientIp, td->clientPort);
                delete td;
                return nullptr;
            }

            void service(int sock, const string& clientIp, const uint16_t& clientPort){
                string who = clientIp + "-" + to_string(clientPort);
                char buffer[1024];
                while(true){
                    ssize_t s = read(sock, buffer, sizeof(buffer)-1);
                    if(s > 0){
                        buffer[s] = 0;
                        string res = func_(buffer);
                        cout << who << ">>> " << res << endl;
                        write(sock, res.c_str(), res.size());
                    }
                    else if(s == 0){
                        close(sock);
                        cout << who << "quit" << endl;
                        break;
                    }
                    else{
                        close(sock);
                        cerr << "read error: " << strerror(errno) << endl;
                        break;
                    }
                }
            }

            ~TcpServer(){

            }

        private:
            uint16_t port_;
            int listenSock_;
            bool quit_;
            func_t func_;
    };
}